/**
 * @file    UART.h
 * Utilisation du port UART du dsPIC avec des buffers en émission et en réception (facultatif).
 * Le module fonctionne avec des interruption et des files (il utilise la librairie ByteFIFO).
 * Seules les fonctions pour l'UART1 sont documentées, l'UART2 s'utilise de façon parfaitement identique.
 * 
 * @author  Zerdligham
 * @sa      ByteFIFO.h
 * @todo    Tester réception bufferisée
 * @version 1.0
*/

#ifndef UART_H
#define UART_H

typedef U8 UART_ERROR;

#define UART_ERROR_OK           0
#define UART_ERROR_OUTOFMEMORY  1
#define UART_ERROR_NOTENOUGHTDATA   3
#define UART_ERROR_BUFFERALMOSTFULL   4
#define UART_ERROR_BUFFEROVERFLOW   5

/**
 * Initialisation du module UART pour une utilisation avec un buffer de transmission et un buffer de réception facultatif
 * @param txPin     N° de la pin utilisée pour la patte TX (4 correspond à RP5 et pas à la pin n°5 du dsPIC)
 * @param rxPin     N° de la pin utilisée pour la patte RX
 * @param baudrate  Vitesse de transmission des données en bps
 * @param txBufferSize  Taille du buffer de transmission.
 * @param rxBufferSize  Taille du buffer de réception, utiliser 0 pour ne pas utiliser de buffer de transmission
 * @param txIntPriority   Priorité de l'interruption de transmission. C'est elle qui lit les données dans le buffer de transmission pour les envoyer réellement, il est donc conseillé de la choisir plutôt haute.
 * @param rxIntPriority   Priorité de l'interruption de réception. Dans le cas où on utilise un buffer en réception, cette intérruption sera charger de lire les données du module pour les placer dans le buffer. Si on n'utilise pas le buffer en réception, il est possible de désactiver l'interruption. Pour cela il suffit de choisir 0 comme priorité.
 * @param intProtect    Niveau de priorité maximal auquel le programme est susceptible d'utiliser le module UART. Ce paramêtre est indispensable pour protéger le module d'évetuelles corruptions de buffers. intProtect doit être au minimum égal au maximum des priorités des interruptions TX et RX.
 * @return  UART_ERROR_OK ou UART_ERROR_OUTOFMEMORY selon que l'initialisation des buffers ait pu se faire dans la mémoire disponible.
 * @warning  L'utilisation correcte des interruptions est indispensable à ce module, voir UART1_onU1TXInterrupt et UART1_onU1RXInterrupt
 */
UART_ERROR UART1_init(U8 txPin, U8 rxPin, U32 baudrate, U16 txBufferSize, U16 rxBufferSize, U8 txIntPriority, U8 rxIntPriority, U8 intProtect);

/**
 * Place des données dans le buffer d'envoi
 * @param tab   données à envoyer
 * @param size  nombre d'octet à envoyer
 * @return  UART_ERROR_OK si la fonction s'est executée normalement, UART_ERROR_BUFFERALMOSTFULL si le buffer d'envoi est plein à plus de 90% (mais dans ce cas, l'envoi s'est bien faire, il ne s'agit pas encore d'une erreur), et UART_ERROR_BUFFEROVERFLOW s'il n'y a pas assez de place dans le buffer. Dans le cas de l'overflow, aucun octet n'est écrit dans le buffer, il est donc très facile de faire une boucle bloquante qui attend que le buffer se vide.
 * Si pour une raison quelquonque on a besoin d'accéder au buffer lui-même (déconseillé, faire attention aux interrputions), il suffit de déclarer la variable <code>extern TabbedFIFO U1TX_buffer;</code>
 */
UART_ERROR UART1_send(const void * tab, U16 size);

/**
 * Place une chaîne de caractères le buffer d'envoi. Cette focntion est strictement équivalente à UART1_send(str, strlen(str)).
 * @param str   chaîne à envoyer
 * @return  UART_ERROR_OK si la fonction s'est executée normalement, UART_ERROR_BUFFERALMOSTFULL si le buffer d'envoi est plein à plus de 90% (mais dans ce cas, l'envoi s'est bien faire, il ne s'agit pas encore d'une erreur), et UART_ERROR_BUFFEROVERFLOW s'il n'y a pas assez de place dans le buffer. Dans le cas de l'overflow, aucun octet n'est écrit dans le buffer, il est donc très facile de faire une boucle bloquante qui attend que le buffer se vide.
 */
UART_ERROR UART1_sendStr(const char * str);

/**
 * Lit des données en provenance de l'UART
 * @param tab   endroit ou stoquer les données
 * @param size  nombre d'octet à lire
 * @return      UART_ERROR_OK si la lecture était possible, ou UART_ERROR_NOTENOUGHTDATA si on a demandé la lecture de plus de données qu'il y en avait disponibles. Dans le second cas, aucun octet n'est lu,
 * Si pour une raison quelquonque on a besoin d'accéder au buffer lui-même (déconseillé, faire attention aux interrputions), il suffit de déclarer la variable <code>extern TabbedFIFO U1RX_buffer;</code>
 */
UART_ERROR UART1_read(void * tab, U16 size);

/**
 * Fonction qui gère la partie envoi du contenu du buffer. Elle doit être appellée depuis l'interrption _U1TXInterrupt.
 * Il est autorisé (quoiqu'a priori déconseillé) d'y faire d'autres choses à partir du moment où cela ne perturbe pas UART1_onU1TXInterrupt.
 */
void UART1_onU1TXInterrupt(void);

/**
 * Fonction qui gère la partie buffering du module s'il est activé. Il faut l'executer depuis l'interrption _U1RXInterrupt, mais seulement si le buffering est activé (sinon on est à peu près certain de cracher le dsPIC).
 * Si on n'utilise pas le buffering, il n'est pas obligatoire d'activer l'interrption (cf UART1_init)
 * Que l'on utilise ou non le buffer, il est possible de traiter les données reçues dans la suite de l'interruption, ou tout simplement de gérer le retour de la fonction.
 * @return UART_ERROR_OK ou UART_ERROR_BUFFERALMOSTFULL si le buffering s'est bien fait (UART_ERROR_BUFFERALMOSTFULL est seulement un avertissement qui indique que 90% du buffer est plein). UART_ERROR_BUFFEROVERFLOW si le buffer est plein et qu'il a été impossible de recopier les données; il y a dans ce cas eu perte de données
 */
UART_ERROR UART1_onU1RXInterrupt(void);

#define UART1_printf(...); {char __msg__[255]; sprintf(__msg__,__VA_ARGS__); UART1_sendStr(__msg__);}
#define UART1_printfBlock(...) {char __msg__[255]; sprintf(__msg__,__VA_ARGS__); while(UART1_sendStr(__msg__)==UART_ERROR_BUFFEROVERFLOW); }


UART_ERROR UART2_init(U8 txPin, U8 rxPin, U32 baudrate, U16 txBufferSize, U16 rxBufferSize, U8 txIntPriority, U8 rxIntPriority, U8 intProtect);
UART_ERROR UART2_send(const void * tab, U16 size);
UART_ERROR UART2_sendStr(const char * str);
UART_ERROR UART2_read(void * tab, U16 size);
void UART2_onU2TXInterrupt(void);
UART_ERROR UART2_onU2RXInterrupt(void);
#define UART2_printf(...); {char __msg__[255]; sprintf(__msg__,__VA_ARGS__); UART2_sendStr(__msg__);}
#define UART2_printfBlock(...) {char __msg__[255]; sprintf(__msg__,__VA_ARGS__); while(UART2_sendStr(__msg__)==UART_ERROR_BUFFEROVERFLOW); }

#endif
